package cn.keniykey.mc.viewlibrary.packet;

import java.util.ArrayList;

import javax.annotation.Nullable;

import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import cn.keniykey.mc.viewlibrary.channel.AbChannelUtil;
import cn.keniykey.mc.viewlibrary.container.api.EContainerType;
import cn.keniykey.mc.viewlibrary.packet.api.AbPacket;
import cn.keniykey.mc.viewlibrary.packet.api.IPacketDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketItemName;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowClick;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowClose;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowCloseDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowDataDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowItemsDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowOpenDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowSetSlotDispatch;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionClass;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionConstructor;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionField;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionMethod;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionUtil;
import cn.keniykey.mc.viewlibrary.reflection.SearchReflectionMethod;

public final class PacketUtilForgeV1_12_1 extends AbPacketUtil{
	private final ReflectionMethod method_bukkit_itemstack_asnmscopy,method_bukkit_craftchatmessage_fromstring;
	private final ReflectionMethod method_nm_itextcomponent_get;
	private final Object obj_nm_itemstack_emptyitem;
	
	private final ReflectionClass class_nm_packet;
	private final ReflectionClass class_nm_packet_in_closewindow,class_nm_packet_in_clickwindow;
	private final ReflectionClass class_nm_packet_in_custompayload;
	
	private final ReflectionConstructor constructor_nm_packet_out_closewindow, constructor_nm_packet_out_openwindw;
	private final ReflectionConstructor constructor_nm_packet_out_windowitems, constructor_nm_nonnulllist;
	private final ReflectionConstructor constructor_nm_packet_out_windowdata, constructor_nm_packet_out_setslot;
	
	private final ReflectionField field_nm_packet_in_closewindow_id;
	private final ReflectionField field_nm_packet_in_clickwindow_id, field_nm_packet_in_clickwindow_slot;
	private final ReflectionField field_nm_packet_in_clickwindow_button, field_nm_packet_in_clickwindow_clicktype;
	private final ReflectionField field_nm_packet_in_custompayload_str;
	
	PacketUtilForgeV1_12_1() {
		this.method_bukkit_itemstack_asnmscopy = ReflectionUtil.findReflectionClass("cb,inventory.CraftItemStack").findMethodByName("asNMSCopy");
		this.obj_nm_itemstack_emptyitem = this.method_bukkit_itemstack_asnmscopy.invoke(new ItemStack(Material.AIR));
		this.method_bukkit_craftchatmessage_fromstring = ReflectionUtil.findReflectionClass("cb,util.CraftChatMessage").findMethod(
				new SearchReflectionMethod().setName("fromString").addParameter(String.class));
		final ReflectionClass class_itextcomponent = ReflectionUtil.findReflectionClass("nm,util.text.ITextComponent");
		this.method_nm_itextcomponent_get = class_itextcomponent.findMethod(new SearchReflectionMethod().addParameter(String.class)
				.setReturn(class_itextcomponent.getClazz()).setStatic(true));
		
		this.class_nm_packet = ReflectionUtil.findReflectionClass("nm,network.Packet");
		this.constructor_nm_packet_out_closewindow = ReflectionUtil.findReflectionClass("nm,network.play.server.SPacketCloseWindow").getConstructor(int.class);
		this.constructor_nm_packet_out_openwindw = ReflectionUtil.findReflectionClass("nm,network.play.server.SPacketOpenWindow").findConstructor(4);
		
		this.constructor_nm_nonnulllist = ReflectionUtil.findReflectionClass("nm,util.NonNullList").findConstructor(2);
		this.constructor_nm_packet_out_windowitems = ReflectionUtil.findReflectionClass("nm,network.play.server.SPacketWindowItems").findConstructor(2);
		this.constructor_nm_packet_out_windowdata = ReflectionUtil.findReflectionClass("nm,network.play.server.SPacketWindowProperty").findConstructor(3);
		this.constructor_nm_packet_out_setslot = ReflectionUtil.findReflectionClass("nm,network.play.server.SPacketSetSlot").findConstructor(3);
		
		this.class_nm_packet_in_closewindow = ReflectionUtil.findReflectionClass("nm,network.play.client,CPacketCloseWindow");
		this.field_nm_packet_in_closewindow_id = this.class_nm_packet_in_closewindow.findField(int.class);
		this.class_nm_packet_in_clickwindow = ReflectionUtil.findReflectionClass("nm,network.play.client.CPacketClickWindow");
		this.field_nm_packet_in_clickwindow_id = this.class_nm_packet_in_clickwindow.findFieldByName("field_149554_a");
		this.field_nm_packet_in_clickwindow_slot = this.class_nm_packet_in_clickwindow.findFieldByName("field_149552_b");
		this.field_nm_packet_in_clickwindow_button = this.class_nm_packet_in_clickwindow.findFieldByName("field_149553_c");
		this.field_nm_packet_in_clickwindow_clicktype = this.class_nm_packet_in_clickwindow.findFieldByName("field_149549_f");
		this.class_nm_packet_in_custompayload = ReflectionUtil.findReflectionClass("nm,network.play.client.CPacketCustomPayload");
		this.field_nm_packet_in_custompayload_str = this.class_nm_packet_in_custompayload.findField(String.class);
	}

	@Override
	public void dispatchPacket(Player player, IPacketDispatch packet, boolean cache) {
		//IPacketDispatch 内的sendPacket实现方法
		Object packet_nm = packet.getCacheReflectionPacket();
		if(packet_nm == null || !this.class_nm_packet.isInstance(packet_nm)) {
			if(packet instanceof PacketWindowCloseDispatch) {
				final PacketWindowCloseDispatch pack = (PacketWindowCloseDispatch) packet;
				packet_nm = this.constructor_nm_packet_out_closewindow.newInstance(pack.getID());
			}else if(packet instanceof PacketWindowItemsDispatch) {
				final PacketWindowItemsDispatch pack = (PacketWindowItemsDispatch) packet;
				final ArrayList<Object> list_items_nm = new ArrayList<>(pack.getItems().size());
				for(final ItemStack item : pack.getItems()) list_items_nm.add(this.coverNMItemStack(item)); //coverNMItemStack已实现判空添加空气物品
				final Object nmlist = this.constructor_nm_nonnulllist.newInstance(list_items_nm, this.obj_nm_itemstack_emptyitem);
				packet_nm = this.constructor_nm_packet_out_windowitems.newInstance(pack.getID(), nmlist);
			}else if(packet instanceof PacketWindowOpenDispatch) {
				final PacketWindowOpenDispatch pack = (PacketWindowOpenDispatch) packet;
				final Object text = this.coverITextComponent(pack.getTitle(), false);
				final String typename = this.coverNMWindowType(pack.getContainerType());
				packet_nm = this.constructor_nm_packet_out_openwindw.newInstance(pack.getID(),typename,text,pack.getSize());
			}else if(packet instanceof PacketWindowSetSlotDispatch) {
				final PacketWindowSetSlotDispatch pack = (PacketWindowSetSlotDispatch) packet;
				final Object nmitem = this.coverNMItemStack(pack.getItemStack());
				packet_nm = this.constructor_nm_packet_out_setslot.newInstance(pack.getID(),pack.getSlot(),nmitem);
			}else if(packet instanceof PacketWindowDataDispatch) {
				final PacketWindowDataDispatch pack = (PacketWindowDataDispatch) packet;
				packet_nm = this.constructor_nm_packet_out_windowdata.newInstance(pack.getID(),pack.getProperty(),pack.getValue());
			}else {
				throw new RuntimeException("Unknown packet dispatch(未知发包类型)");
			}
			if(cache) packet.setCacheReflectionPacket(packet_nm);
		}
		AbChannelUtil.getUtil().sendPacket(player, packet_nm);
	}

	private String coverNMWindowType(EContainerType type) {
		switch(type) {
		case GENERIC_1X9:
		case GENERIC_2X9:
		case GENERIC_3X9:
		case GENERIC_4X9:
		case GENERIC_5X9:
		case GENERIC_6X9:
			return "minecraft:container";
		case GENERIC_3X3:
		case HOPPER:
			return "minecraft:hopper";
		case ANVIL:
			return "minecraft:anvil";
		case CRAFTING_TABLE:
			return "minecraft:crafting_table";
		case ENCHANTMENT_TABLE:
			return "minecraft:enchanting_table";
		default:
			throw new RuntimeException("Unknown EContainerType(在当前版本属于未知界面类型)");
		}
	}
	private Object coverITextComponent(String str, boolean craft) {
		if(!craft) return this.method_nm_itextcomponent_get.invoke(str);
		final Object[] objs = (Object[]) this.method_bukkit_craftchatmessage_fromstring.invoke(str);
		return objs[0];
	}
	private Object coverNMItemStack(@Nullable ItemStack item) {
		if(item == null || item.getType().equals(Material.AIR)) return this.obj_nm_itemstack_emptyitem;
		return this.method_bukkit_itemstack_asnmscopy.invoke(item);
	}
	@Nullable
	@Override
	public AbPacket coverPacket(Object nmpacket, int windowid) {
		if(this.class_nm_packet_in_closewindow.isInstance(nmpacket)) {
			final int id = (int) this.field_nm_packet_in_closewindow_id.dynamicGet(nmpacket);
			if(windowid != id) return null;
			return new PacketWindowClose(id);
		}
		if(this.class_nm_packet_in_clickwindow.isInstance(nmpacket)) {
			final int id = (int) this.field_nm_packet_in_clickwindow_id.dynamicGet(nmpacket);
			if(windowid != id) return null;
			final String mode = this.field_nm_packet_in_clickwindow_clicktype.dynamicGet(nmpacket).toString();
			final int button = (int) this.field_nm_packet_in_clickwindow_button.dynamicGet(nmpacket);
			final int slot = (int) this.field_nm_packet_in_clickwindow_slot.dynamicGet(nmpacket);
			return new PacketWindowClick(windowid, mode, button, slot);
		}
		if(this.class_nm_packet_in_custompayload.isInstance(nmpacket)) {
			final String str = (String) this.field_nm_packet_in_custompayload_str.dynamicGet(nmpacket);
			return new PacketItemName(str);
		}
		return null;
	}

}